/* Copyright (c) Microsoft Corporation.
   Licensed under the MIT License. */

/***************************************************************************

	msnd.h: Movie sound class

	Primary Authors: *****, *****
	Status:  Reviewed

	BASE ---> BACO ---> MSND
	BASE ---> CMH  ---> MSQ

	NOTE: when the MSQ stops sounds, it does it based on sound class (scl)
	and not sound queue (sqn).  This is slightly less efficient, because the
	SNDM must search all open sound queues for the given scl's when we stop
	sounds; however, the code is made much simpler, because the sqn is
	generated on the fly based on whether the sound is for an actor or
	background, the sty of the sound, and (in the case of actor sounds) the
	arid of the source of the sound.  If we had to enumerate all sounds
	based on that information, we'd wind up calling into the SNDM a minimum
	of three times, plus three times for each actor; not only is the
	enumeration on this side inefficient (the MSQ would have to call into the
	MVIE to enumerate all the known actors), but the number of calls to SNDM
	gets to be huge!  On top of all that, we'd probably wind up finding some
	bugs where a sound is still playing for an actor that's been deleted, and
	possibly fail to stop the sound properly (Murphy reigning strong in any
	software project).

***************************************************************************/
#ifndef MSND_H
#define MSND_H

// Sound types
enum
	{	
	styNil = 0,
	styUnused, 			// Retain.  Existing content depends on subsequent values
	stySfx,
	stySpeech,
	styMidi,
	styLim
	};

// Sound-class-number constants
const long sclNonLoop = 0;
const long sclLoopWav = 1;
const long sclLoopMidi = 2;

#define vlmNil (-1)

// Sound-queue-number constants
enum
	{
	sqnActr =  0x10000000,
	sqnBkgd = 0x20000000,
	sqnLim
	};
#define ksqnStyShift 	16;	  	// Shift for the sqnsty

// Sound Queue Delta times
// Any sound times less than ksqdtimLong will be clocked & stopped
const long kdtimOffMsq = 0;
const long kdtimLongMsq = klwMax;
const long kdtim2Msq = ((kdtimSecond *2) * 10) / 12;	// adjustment -> 2 seconds
const long kSndSamplesPerSec = 22050;
const long kSndBitsPerSample = 8;
const long kSndBlockAlign = 1;
const long kSndChannels = 1;

/****************************************

	Movie Sound on file

****************************************/
struct MSNDF
	{
	short bo;
	short osk;
	long sty;			// sound type
	long vlmDefault;	// default volume
	bool fInvalid;		// Invalid flag
	};
const BOM kbomMsndf = 0x5FC00000;

const CHID kchidSnd = 0;  // Movie Sound sound/music

// Function to stop all movie sounds.
inline void StopAllMovieSounds(void)
{
	vpsndm->StopAll(sqnNil, sclNonLoop);
	vpsndm->StopAll(sqnNil, sclLoopWav);
	vpsndm->StopAll(sqnNil, sclLoopMidi);
}

/****************************************

	The Movie Sound class

****************************************/
typedef class MSND *PMSND;
#define MSND_PAR BACO
#define kclsMSND 'MSND'
class MSND : public MSND_PAR
	{
	RTCLASS_DEC
	ASSERT
	MARKMEM

protected:
	// these are inherent to the msnd
	CTG _ctgSnd;	// CTG of the WAV or MIDI chunk
	CNO _cnoSnd;	// CNO of the WAV or MIDI chunk
	PRCA _prca;		// file that the WAV/MIDI lives in
	long _sty;		// MIDI, speech, or sfx
	long _vlm;		// Volume of the sound
	bool _fNoSound;	// Set if silent sound
	STN _stn;		// Sound name
	bool _fInvalid;	// Invalid flag

protected:
	bool _FInit(PCFL pcfl, CTG ctg, CNO cno);

public:
    static bool FReadMsnd(PCRF pcrf, CTG ctg, CNO cno, PBLCK pblck,
    	PBACO *ppbaco, long *pcb);
	static bool FGetMsndInfo(PCFL pcfl, CTG ctg, CNO cno, bool *pfInvalid = pvNil,
		long *psty = pvNil, long *pvlm = pvNil);
	static bool FCopyMidi(PFIL pfilSrc, PCFL pcflDest, CNO *pcno, PSTN pstn = pvNil);
	static bool FWriteMidi(PCFL pcflDest, PMIDS pmids, STN *pstnName, CNO *pcno);
	static bool FCopyWave(PFIL pfilSrc, PCFL pcflDest, long sty, CNO *pcno, PSTN pstn = pvNil);
	static bool FWriteWave(PFIL pfilSrc, PCFL pcflDest, long sty, STN *pstnName, CNO *pcno);
	~MSND(void);

	static long SqnActr(long sty, long objID);
	static long SqnBkgd(long sty, long objID);
	long Scl(bool fLoop) { return (fLoop ? ((_sty == styMidi) ? sclLoopMidi : sclLoopWav) : sclNonLoop);}
	long SqnActr(long objID) { AssertThis(0); return SqnActr(_sty, objID); }
	long SqnBkgd(long objID) { AssertThis(0); return SqnBkgd(_sty, objID); }

	bool FInvalidate(void);
	bool FValid(void) { AssertBaseThis(0); return FPure(!_fInvalid); }
	PSTN Pstn(void) { AssertThis(0); return &_stn; }
	long Sty(void) { AssertThis(0); return _sty; }
	long Vlm(void) { AssertThis(0); return _vlm; }
 	long Spr(long tool);		// Return Priority
	long FNoSound(void) { AssertThis(0); return _fNoSound; }

	void Play(long objID, bool fLoop, bool fQueue, long vlm,
		long spr, bool fActr = fFalse, ulong dtsStart = 0);

	};

/****************************************

	Movie Sound Queue  (MSQ)
	Sounds to be played at one time.
	These are of all types, queues &
	classes

****************************************/
typedef class MSQ *PMSQ;
#define MSQ_PAR CMH
#define kclsMSQ 'MSQ'

const long kcsqeGrow = 10;						// quantum growth for sqe

// Movie sound queue entry
struct SQE
	{
	long objID;	  		// Unique identifier (actor id, eg)
	bool fLoop;			// Looping sound flag
	bool fQueue;		// Queued sound
	long vlmMod; 		// Volume modification
	long spr; 			// Priority
	bool fActr;			// Actor vs Scene (to generate unique class)
	PMSND pmsnd;		// PMSND
	ulong dtsStart;		// How far into the sound to start playing
	};						

class MSQ : public MSQ_PAR
	{
	RTCLASS_DEC
	ASSERT
	MARKMEM
	CMD_MAP_DEC(MSQ)

protected:
	PGL _pglsqe;		// Sound queue entries
	long _dtim;			// Time sound allowed to play
	PCLOK _pclok;

public:
	MSQ(long hid) : MSQ_PAR(hid) {}
	~MSQ(void);

	static PMSQ PmsqNew(void);
	
	bool FEnqueue(PMSND pmsnd, long objID, bool fLoop, bool fQueue,
		long vlm, long spr, bool fActr = fFalse, ulong dtsStart = 0, 
		bool fLowPri = fFalse);
	void PlayMsq(void);		// Destroys queue as it plays
	void FlushMsq(void);	// Without playing the sounds
	bool FCmdAlarm(PCMD pcmd);

	// Sound on/off & duration control
	void SndOff(void) {AssertThis(0); _dtim = kdtimOffMsq;}
	void SndOnShort(void) {AssertThis(0); _dtim = kdtim2Msq;}
	void SndOnLong(void) {AssertThis(0); _dtim = kdtimLongMsq;}	
	void StopAll(void)
	{
		if (pvNil != _pclok)
			_pclok->Stop();

		StopAllMovieSounds();
	}
	bool FPlaying(bool fLoop) {AssertThis(0);
		return (fLoop ? (vpsndm->FPlayingAll(sqnNil, sclLoopMidi) ||  vpsndm->FPlayingAll(sqnNil, sclLoopWav)) :
						vpsndm->FPlayingAll(sqnNil, sclNonLoop)); }

	// Save/Restore snd-on duration times
	long DtimSnd(void) {AssertThis(0); return _dtim;}
	void SndOnDtim(long dtim) {AssertThis(0); _dtim = dtim;}
};

#endif MSND_H
